local assets = {
    Asset("ANIM", "anim/mami_projectiles.zip"),
}

local ATTACK_MUST_TAGS = { "monster", "hostile" }
local ATTACK_ONEOF1_TAGS = { "_combat" }
local ATTACK_ONEOF2_TAGS = { "_combat", "CHOP_workable", "MINE_workable" }
local ATTACK_ONEOF3_TAGS = { "_combat", "CHOP_workable", "MINE_workable", "HAMMER_workable" }
local ATTACK_ONEOF4_TAGS = { "_combat", "CHOP_workable", "MINE_workable", "HAMMER_workable", "DIG_workable" }
local ATTACK_CANT_TAGS = { "companion", "witchfriend", "witch" }
local function GetPlayerAttackTarget(attacker, radius, pos, force, work_level)
    local targets = {}
    pos = pos or attacker:GetPosition()
    attacker.components.combat.ignorehitrange = true
    local oneof_tags = work_level == 4 and ATTACK_ONEOF4_TAGS
        or work_level == 3 and ATTACK_ONEOF3_TAGS
        or work_level and ATTACK_ONEOF2_TAGS
        or ATTACK_ONEOF1_TAGS

    for _, v in ipairs(TheSim:FindEntities(pos.x, pos.y, pos.z, radius, nil, ATTACK_CANT_TAGS, oneof_tags)) do
        if v.entity:IsVisible()
            and ((attacker.components.combat:CanTarget(v)
                    and (force or v:HasOneOfTags(ATTACK_MUST_TAGS)
                        or (v.components.combat and v.components.combat.target and v.components.combat.target:HasOneOfTags(ATTACK_CANT_TAGS))))
                or (v.components.workable and v.components.workable:CanBeWorked()))
        then
            table.insert(targets, v)
        end
    end
    attacker.components.combat.ignorehitrange = false
    return targets
end

--生成本体的暗影
local function SpawnMinion(target)
    local ent = SpawnSaveRecord(target:GetSaveRecord())

    if ent.AnimState then
        ent.AnimState:SetMultColour(0, 0, 0, 0.6)
    end

    ent:AddTag("mami_shadowminion")
    -- enter:AddTag("companion") --加不加呢
    ent._mami_identity = target
    target._mami_minion = ent

    ent.components.combat.externaldamagemultipliers:SetModifier(ent, 0.5)
    ent.components.combat:SetTarget(target)
    ent:DoPeriodicTask(0, function()
        if ent.components.combat then
            ent.components.combat:SetTarget(target)
        end
    end)
    ent:ListenForEvent("onremove", function() ent:Remove() end, target)
    ent:ListenForEvent("death", function() ent.components.health:Kill() end, target)
    ent:ListenForEvent("onremove", OnMinionRemove)
    ent:ListenForEvent("healthdelta", OnMinionHealthDelta)
    target:ListenForEvent("healthdelta", OnMinionHealthDelta)
    ent.persists = false

    if not ent.components.vanish_on_sleep then
        ent:AddComponent("vanish_on_sleep") --脱离加载范围消失
    end
    if ent.components.lootdropper then
        ent.components.lootdropper:SetLoot(nil)
        ent.components.lootdropper:SetChanceLootTable(nil) --无掉落
    end
end

local SHADOW_CANT_TAGS = { "mami_shadowminion", "structure", "mami_cant_shadow" }

local function MamiSkillDoAttack(self, targ, damage, truedamage)
    if not targ then return end --稳妥起见

    self.ignorehitrange = true
    self.skilldamage = true
    self.mami_truedamage = truedamage and damage or nil

    --使用技能攻击杯束缚的敌人伤害增加
    if targ.prefab == "mami_tie_fx"
        and self.inst.components.skilltreeupdater and self.inst.components.skilltreeupdater:IsActivated("battlemaster2")
    then
        damage = damage * 1.3
    end

    local defaultdamage = self.defaultdamage
    self:SetDefaultDamage(damage * (self.inst.components.mami_ability and self.inst.components.mami_ability:GetSkillMult() or 1))
    self.mami_ignoreweapon = true --我不希望伤害被装备的武器影响
    -- print("攻击目标", targ or self.target, damage)
    self:DoAttack(targ)
    self.mami_ignoreweapon = false
    self:SetDefaultDamage(defaultdamage)

    self.skilldamage = nil
    self.ignorehitrange = false
    self.mami_truedamage = nil

    if self.inst.components.skilltreeupdater and self.inst.components.skilltreeupdater:IsActivated("beautywitch") then
        if targ
            and not targ:HasOneOfTags(SHADOW_CANT_TAGS)
            and targ.components.combat
            and not IsEntityDead(targ, true)
            and targ.components.health.currenthealth ~= targ.components.health.minhealth --最低血量的生物不应该生成，噩梦疯猪战斗前就是1滴血
            and (not targ._mami_minion or not targ._mami_minion:IsValid())
        then
            SpawnMinion(targ)
        end
    end
end

local function OnHit(inst, _, target)
    local attacker = inst.attacker
    if target and attacker and attacker:IsValid() and attacker.components.combat then
        if inst.work_level and target.components.workable then
            target.components.workable:Destroy(attacker)
        else
            local weapon = inst.components.complexprojectile.owningweapon
            local damage = weapon.components.weapon and weapon.components.weapon:GetDamage(attacker, target) or nil
            if damage then
                MamiSkillDoAttack(attacker.components.combat, target, damage, inst.armor)
            end
        end

        if attacker:HasTag("mami_tie_bullet") then --束缚弹
            attacker:RemoveTag("mami_tie_bullet")
            local fx = SpawnPrefab("mami_tie_bullet2")
            fx.Transform:SetPosition(target.Transform:GetWorldPosition())
            fx:Boom()
        end
    end
    inst.hitcount = inst.hitcount - 1
    if not target or inst.hitcount <= 0 then
        inst:Remove()
    end
end

local function OnLaunch(inst)
    inst.components.complexprojectile.velocity.y = 0
end

local function OnUpdate(inst)
    local dt = 0.05
    local self = inst.components.complexprojectile

    if inst.attacker:IsValid() and inst.attacker.components.combat then
        local pos = inst:GetPosition()
        for _, v in ipairs(GetPlayerAttackTarget(inst.attacker, inst.range + 2, pos, true, inst.work_level)) do
            local vpos = v:GetPosition()
            if not inst.targets[v]
                and v:GetPhysicsRadius(0) + inst.range > VecUtil_Dist(pos.x, pos.z, vpos.x, vpos.z) --希望把高度差忽略掉，更关心水平面上的距离差
                and (not inst.target or inst.target == v)
            then
                inst.targets[v] = true
                if self.onhitfn ~= nil then
                    self.onhitfn(inst, inst.attacker, v)
                end
            end
        end
    end

    inst.Physics:SetMotorVel(self.velocity:Get())
    self.velocity.x = self.velocity.x + (self.gravity * dt)
    if self.velocity.x < inst.minspeed then
        self:Hit() --落空
    end
    return true
end


---根据当前朝向和前进距离计算最终落点
local function GetPositionForward(inst, distance)
    if inst == nil then
        return nil
    end

    local rot = inst:GetRotation() * DEGREES
    local pos = inst:GetPosition()
    return pos + Vector3(distance * math.cos(rot), 0, -distance * math.sin(rot)) --z轴需要反过来
end

---初始化
---@param inst Entity
---@param attacker Entity
---@param data table|nil targetPos,owningweapon,gravity,speed,minspeed,hitcount,damage,range
local function Setup(inst, attacker, data)
    data = data or {}

    inst.minspeed = data.minspeed or inst.minspeed or 0
    inst.hitcount = data.hitcount == -1 and math.huge
        or data.hitcount
        or inst.hitcount
        or 1
    inst.targets = {}
    inst.range = data.range or 0
    inst.work_level = data.work_level
    inst.armor = data.armor
    inst.spdamage = data.spdamage
    inst.attacker = attacker
    inst.target = data.target            --如果有target，则只攻击target

    local owner = data.owner or attacker --owner是发射物，但攻击者（玩家）不一定是发射者
    inst.Transform:SetPosition(owner.Transform:GetWorldPosition())
    local targetPos = data.targetPos or GetPositionForward(owner, 8)
    if data.damage then
        inst.components.weapon:SetDamage(data.damage)
    end
    if data.gravity then
        inst.components.complexprojectile:SetGravity(data.gravity)
    end
    inst.components.complexprojectile:SetHorizontalSpeed(data.speed or 35)
    inst.components.complexprojectile:SetLaunchOffset(data.offset)
    inst.components.complexprojectile:Launch(targetPos, owner, data.owningweapon or inst)

    return inst
end

local function common(anim, ground)
    local inst = CreateEntity()

    inst.entity:AddTransform()
    inst.entity:AddAnimState()
    inst.entity:AddSoundEmitter()
    inst.entity:AddNetwork()

    MakeInventoryPhysics(inst)
    RemovePhysicsColliders(inst)

    inst.AnimState:SetBank("mami_projectiles")
    inst.AnimState:SetBuild("mami_projectiles")
    inst.AnimState:PlayAnimation(anim, true)
    inst.AnimState:SetBloomEffectHandle("shaders/anim.ksh")
    if ground then
        inst.AnimState:SetOrientation(ANIM_ORIENTATION.OnGround)
        inst.AnimState:SetSortOrder(3)
    end

    inst:AddTag("FX")
    inst:AddTag("NOCLICK")

    inst.entity:SetCanSleep(false)

    inst.entity:SetPristine()

    if not TheWorld.ismastersim then
        return inst
    end

    inst.Setup = Setup

    inst:AddComponent("weapon")

    inst:AddComponent("complexprojectile")
    inst.components.complexprojectile.usehigharc = false
    inst.components.complexprojectile:SetOnHit(OnHit)
    inst.components.complexprojectile:SetOnLaunch(OnLaunch)
    inst.components.complexprojectile:SetOnUpdate(OnUpdate)

    inst.persists = false

    return inst
end

local function mami_projectile_oneshot_fn()
    local inst = common("oneshot", true)
    inst.AnimState:SetScale(1.2, 1.2)

    return inst
end

return Prefab("mami_projectile_oneshot", mami_projectile_oneshot_fn, assets)
